{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit Bars;

interface

uses Classes, UIObjects, XMLHelper, AHMTypes;

{$IFDEF DESIGNER}
const
  // Property categories and hints for designer
  CAT_DESIGN = 'Design';
  CAT_BGIMAGES = 'Background';
  CAT_FGIMAGES = 'Foreground';
  HINT_BARTYPE = 'specifies the type of bar (progress, scroll or slider)';
  HINT_TOPMARGIN = 'specifies top margin between background and foreground';
  HINT_BOTTOMMARGIN = 'specifies bottom margin between background and foreground';
  HINT_LEFTMARGIN = 'specifies left margin between background and foreground';
  HINT_RIGHTMARGIN = 'specifies right margin between background and foreground';
  HINT_WIDTH = 'specifies width for the bar';
  HINT_HEIGHT = 'specifies height for the bar';
  HINT_BGIMAGESTL = 'defines background bar top/left bitmap or shape for each bar state';
  HINT_BGIMAGESM = 'defines background bar middle bitmap or shape for each bar state';
  HINT_BGIMAGESBR = 'defines background bar bottom/right bitmap or shape for each bar state';
  HINT_FGIMAGESTL = 'defines foreground bar top/left bitmap or shape for each bar state';
  HINT_FGIMAGESM = 'defines foreground bar middle bitmap or shape for each bar state';
  HINT_FGIMAGESBR = 'defines foreground bar bottom/right bitmap or shape for each bar state';
{$ENDIF}

type
  TAHMBarType = (btProgress, btScroll, btSlider);

  TAHMAbstractBar = class(TAHMUIDisplayObject)
  private
    FLeftMargin: Single;
    FRightMargin: Single;
    FTopMargin: Single;
    FBottomMargin: Single;
    FVirtLeftMargin: Integer;
    FVirtRightMargin: Integer;
    FVirtTopMargin: Integer;
    FVirtBottomMargin: Integer;
    FBarType: TAHMBarType;
    FImagesBgTL, FImagesBgM, FImagesBgBR: TAHMUIStateObjectList;
    FImagesFgTL, FImagesFgM, FImagesFgBR: TAHMUIStateObjectList;
  protected
    procedure SetVirtualTopMargin(Margin: Integer);
    procedure SetVirtualBottomMargin(Margin: Integer);
    procedure SetVirtualLeftMargin(Margin: Integer);
    procedure SetVirtualRightMargin(Margin: Integer);
    function GetImages(const ImageType: String): TAHMUIStateObjectList;
    procedure AddImage(State, ImageType, ObjectName: String; ObjectType: TAHMUIObjectType);
    procedure ClearProperties; override;
  public
    constructor Create(const Name: String); override;
    destructor Destroy; override;
    property TopMargin: Single read FTopMargin write FTopMargin;
    property BottomMargin: Single read FBottomMargin write FBottomMargin;
    property LeftMargin: Single read FLeftMargin write FLeftMargin;
    property RightMargin: Single read FRightMargin write FRightMargin;
    procedure ConfigurePreview(Control: TObject); override;
    procedure Render(Control: TObject; iX, iY, iWidth, iHeight: Single); override;
    procedure SeedObject; override;
    procedure Assign(Source: TPersistent); override;
    procedure SaveXMLBody(Lines: TStrings); override;
    procedure ParseFromXML(XML: TAHMXMLHelper); override;
    procedure BuildProperties(Properties: TAHMUIPropertyList); override;
    class function ClassName: ShortString; override;
  published
    property BarType: TAHMBarType read FBarType write FBarType;
    property VirtualTopMargin: Integer read FVirtTopMargin write SetVirtualTopMargin;
    property VirtualBottomMargin: Integer read FVirtBottomMargin write SetVirtualBottomMargin;
    property VirtualLeftMargin: Integer read FVirtLeftMargin write SetVirtualLeftMargin;
    property VirtualRightMargin: Integer read FVirtRightMargin write SetVirtualRightMargin;
    property ImagesBgTL: TAHMUIStateObjectList read FImagesBgTL;
    property ImagesBgM: TAHMUIStateObjectList read FImagesBgM;
    property ImagesBgBR: TAHMUIStateObjectList read FImagesBgBR;
    property ImagesFgTL: TAHMUIStateObjectList read FImagesFgTL;
    property ImagesFgM: TAHMUIStateObjectList read FImagesFgM;
    property ImagesFgBR: TAHMUIStateObjectList read FImagesFgBR;
    // Publish inherited properties
    property VirtualWidth;
    property VirtualHeight;
  end;

  TAHMAbstractBarList = class(TAHMUIObjectList)
  private
  protected
  public
    constructor Create; override;
    function AddAbstractBar(const InheritFrom, NewName: String): TAHMAbstractBar;
    function GetAbstractBar(const Name: String): TAHMAbstractBar;
    procedure PopulateDefaultObjects; override;
    class function ClassName: ShortString; override;
  end;


implementation

uses SysUtils, ApplicationManager, Screen, BaseControls, UIControls, Bitmaps,
     Shapes, MetaConsts, TagNames, XMLTags, ErrorCodes;

// TAHMAbstractBarList

constructor TAHMAbstractBarList.Create;
begin
  inherited Create;

  XMLTagName := TAG_BARS;
  ChildClass := TAHMAbstractBar;
end;

function TAHMAbstractBarList.AddAbstractBar(const InheritFrom, NewName: String): TAHMAbstractBar;
begin
  Result := TAHMAbstractBar(Inherit(InheritFrom, NewName));
end;

function TAHMAbstractBarList.GetAbstractBar(const Name: String): TAHMAbstractBar;
begin
  Result := TAHMAbstractBar(GetObject(Name));
end;

class function TAHMAbstractBarList.ClassName: ShortString;
begin
  // Remove 'Abstract' prefix
  Result := 'Bar';
end;

procedure TAHMAbstractBarList.PopulateDefaultObjects;
begin
  with ApplicationManagerInstance.ThemeManager, DefaultObject as TAHMAbstractBar do
  begin
    VirtualWidth := DEFAULT_CTRL_WIDTH;
    VirtualHeight := DEFAULT_CTRL_HEIGHT div 4;
    ImagesFgTL.AddObject(STATE_VAL_IDLE, ThemeObjects[otShapes].DefaultObjects[TAG_BAR]);
    ImagesBgM.AddObject(STATE_VAL_IDLE, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_IDLE]);
    ImagesBgM.AddObject(STATE_VAL_FOCUS, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_FOCUS]);
    ImagesBgM.AddObject(STATE_VAL_PRESS, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_PRESS]);
    ImagesBgM.AddObject(STATE_VAL_DISABLE, ThemeObjects[otShapes].DefaultObjects[STATE_VAL_DISABLE]);
  end;
end;

//----------------------------------------------------------------------------//

// TAHMAbstractBar

constructor TAHMAbstractBar.Create(const Name: String);
begin
  inherited Create(Name);

  XMLTagName := TAG_BAR;
  ObjectType := otBars;
  ImageIndex := Ord(otBars);

  FImagesBgTL := TAHMUIStateObjectList.Create;
  FImagesBgTL.ObjectTypes := [otBitmaps, otShapes];
  FImagesBgTL.StateType := stControl;
  FImagesBgTL.Owner := Self;
  FImagesBgM := TAHMUIStateObjectList.Create;
  FImagesBgM.ObjectTypes := [otBitmaps, otShapes];
  FImagesBgM.StateType := stControl;
  FImagesBgM.Owner := Self;
  FImagesBgBR := TAHMUIStateObjectList.Create;
  FImagesBgBR.ObjectTypes := [otBitmaps, otShapes];
  FImagesBgBR.StateType := stControl;
  FImagesBgBR.Owner := Self;
  FImagesFgTL := TAHMUIStateObjectList.Create;
  FImagesFgTL.ObjectTypes := [otBitmaps, otShapes];
  FImagesFgTL.StateType := stControl;
  FImagesFgTL.Owner := Self;
  FImagesFgM := TAHMUIStateObjectList.Create;
  FImagesFgM.ObjectTypes := [otBitmaps, otShapes];
  FImagesFgM.StateType := stControl;
  FImagesFgM.Owner := Self;
  FImagesFgBR := TAHMUIStateObjectList.Create;
  FImagesFgBR.ObjectTypes := [otBitmaps, otShapes];
  FImagesFgBR.StateType := stControl;
  FImagesFgBR.Owner := Self;

{$IFDEF DESIGNER}
  FImagesBgTL.DisplayClassName := 'Background top/left image';
  FImagesBgM.DisplayClassName := 'Background middle image';
  FImagesBgBR.DisplayClassName := 'Background bottom/right image';
  FImagesFgTL.DisplayClassName := 'Foreground top/left image';
  FImagesFgM.DisplayClassName := 'Foreground middle image';
  FImagesFgBR.DisplayClassName := 'Foreground bottom/right image';

  // Render preview using a bar control
  PreviewControlClass := TAHMBarControl;
{$ENDIF}
end;

destructor TAHMAbstractBar.Destroy;
begin
  FImagesBgTL.Free;
  FImagesBgM.Free;
  FImagesBgBR.Free;
  FImagesFgTL.Free;
  FImagesFgM.Free;
  FImagesFgBR.Free;

  inherited Destroy;
end;

procedure TAHMAbstractBar.SetVirtualTopMargin(Margin: Integer);
begin
  FVirtTopMargin := Margin;
  FTopMargin := ScreenInstance.ScaleVirtualY(Margin);
end;

procedure TAHMAbstractBar.SetVirtualBottomMargin(Margin: Integer);
begin
  FVirtBottomMargin := Margin;
  FBottomMargin := ScreenInstance.ScaleVirtualY(Margin);
end;

procedure TAHMAbstractBar.SetVirtualLeftMargin(Margin: Integer);
begin
  FVirtLeftMargin := Margin;
  FLeftMargin := ScreenInstance.ScaleVirtualX(Margin);
end;

procedure TAHMAbstractBar.SetVirtualRightMargin(Margin: Integer);
begin
  FVirtRightMargin := Margin;
  FRightMargin := ScreenInstance.ScaleVirtualX(Margin);
end;

function TAHMAbstractBar.GetImages(const ImageType: String): TAHMUIStateObjectList;
begin
  // Map graphic name to appropriate bitmap state list
  if ImageType = BARIMG_VAL_BGSTART then Result := FImagesBgTL
  else if ImageType = BARIMG_VAL_BGMIDDLE then Result := FImagesBgM
  else if ImageType = BARIMG_VAL_BGFINISH then Result := FImagesBgBR
  else if ImageType = BARIMG_VAL_FGSTART then Result := FImagesFgTL
  else if ImageType = BARIMG_VAL_FGMIDDLE then Result := FImagesFgM
  else if ImageType = BARIMG_VAL_FGFINISH then Result := FImagesFgBR
  else Result := nil;
end;

procedure TAHMAbstractBar.AddImage(State, ImageType, ObjectName: String; ObjectType: TAHMUIObjectType);
var
  Images: TAHMUIStateObjectList;
  UIObject: TAHMUIObject;
begin
  // Get state list for appropriate image type
  Images := GetImages(ImageType);
  if Assigned(Images) then
    with ApplicationManagerInstance.ThemeManager do
    begin
      UIObject := ThemeObjects[ObjectType].GetObject(ObjectName);
      if Assigned(UIObject) then Images.AddObject(State, UIObject);
    end;
end;

procedure TAHMAbstractBar.ClearProperties;
begin
  inherited;

  // Clear custom bar properties
  VirtualTopMargin := 0;
  VirtualBottomMargin := 0;
  VirtualLeftMargin := 0;
  VirtualRightMargin := 0;
  FBarType := btProgress;
  FImagesBgTL.Clear;
  FImagesBgM.Clear;
  FImagesBgBR.Clear;
  FImagesFgTL.Clear;
  FImagesFgM.Clear;
  FImagesFgBR.Clear;
end;

procedure TAHMAbstractBar.ConfigurePreview(Control: TObject);
begin
{$IFDEF DESIGNER}
  with Control as TAHMBarControl do
  begin
    // Setup metadata for bar
    MetaValue := META_PREVIEW_BAR_VALUE;
    MetaGroup := META_PREVIEW_BAR_MAX;

    // Don't animate if user is manually dragging
    if Pressed then Exit;

    // MetaIndex is used to store the movement direction
    if Position = Maximum then MetaIndex := 'down'
    else if Position = 0 then MetaIndex := 'up';

    // Increment metadata value to animate bar control for preview
    with ApplicationManagerInstance do
      if MetaIndex = 'up' then MetaDataManager.IncMeta(MetaValue, MetaGroup)
                          else MetaDataManager.DecMeta(MetaValue, MetaGroup);
  end;
{$ENDIF}
end;

procedure TAHMAbstractBar.Render(Control: TObject; iX, iY, iWidth, iHeight: Single);
var
  LengthTL, LengthM, LengthBR: Single;
  BarX, BarY, BarWidth, BarHeight, BarPos: Single;
  BarMax: Integer;
  Rect: TAHMRectF;
  Horizontal: Boolean;
  GfxBgTL, GfxBgM, GfxBgBR, GfxFgTL, GfxFgM, GfxFgBR: TAHMUIDisplayObject;
begin
  // Pre-render our control if necessary
  inherited;

  with Control as TAHMBarControl do
  begin
    // Lookup & cache objects for various bar textures we'll be using
    GfxBgTL := TAHMUIDisplayObject(FImagesBgTL.GetStateObject(ControlState, Checked));
    GfxBgM  := TAHMUIDisplayObject(FImagesBgM .GetStateObject(ControlState, Checked));
    GfxBgBR := TAHMUIDisplayObject(FImagesBgBR.GetStateObject(ControlState, Checked));
    GfxFgTL := TAHMUIDisplayObject(FImagesFgTL.GetStateObject(ControlState, Checked));
    GfxFgM  := TAHMUIDisplayObject(FImagesFgM .GetStateObject(ControlState, Checked));
    GfxFgBR := TAHMUIDisplayObject(FImagesFgBR.GetStateObject(ControlState, Checked));

    // Get bar position and maximum properties
    BarPos := RenderValue;
    BarMax := Maximum;

    // For sliders we need to invert the position prior to rendering
    if FBarType = btSlider then BarPos := BarMax - BarPos;

    // Adjust position & maximum if max is a negative value
    if BarMax < 0 then
    begin
      BarMax := Abs(BarMax);
      BarPos := BarPos + BarMax;
    end;

    // Use overall control dimensions if set, otherwise use bar definition
    if (iWidth <> 0) and (iHeight <> 0) then
      Rect := CanvasRect(iX, iY, iWidth, iHeight)
    else
      Rect := CanvasRect(iX, iY, Self.Width, Self.Height);

    // Calculate central bar position
    BarWidth := Rect.Width - FLeftMargin - FRightMargin;
    if BarWidth < 0 then BarWidth := 0;
    BarHeight := Rect.Height - FTopMargin - FBottomMargin;
    if BarHeight < 0 then BarHeight := 0;
    BarX := iX + FLeftMargin;
    BarY := iY + FTopMargin;

    // Determine whether bar should be rendered vertically or horizontally
    Horizontal := (Rect.Width > Rect.Height);

    // Do we want to draw a background?
    if Assigned(GfxBgM) or Assigned(GfxBgTL) or Assigned(GfxBgBR) then
    begin
      // Determine width/height of top/left and bottom/right bar background
      if Assigned(GfxBgTL) then
        if Horizontal then LengthTL := GfxBgTL.Width else LengthTL := GfxBgTL.Height
      else LengthTL := 0;
      if Assigned(GfxBgBR) then
        if Horizontal then LengthBR := GfxBgBR.Width else LengthBR := GfxBgBR.Height
      else LengthBR := 0;

      // Determine width/height of central bar background
      if Horizontal then LengthM := Rect.Width else LengthM := Rect.Height;
      LengthM := LengthM - LengthTL - LengthBR;
      if LengthM < 0 then LengthM := 0;

      // Render bar background
      if LengthTL > 0 then
      begin
        if Horizontal then
          GfxBgTL.Render(Control, iX, iY, LengthTL, Rect.Height)
        else
          GfxBgTL.Render(Control, iX, iY, Rect.Width, LengthTL);
      end;
      if Assigned(GfxBgM) and (LengthM > 0) then
      begin
        if Horizontal then
          GfxBgM.Render(Control, iX + LengthTL, iY, LengthM, Rect.Height)
        else
          GfxBgM.Render(Control, iX, iY + LengthTL, Rect.Width, LengthM);
      end;
      if LengthBR > 0 then
      begin
        if Horizontal then
          GfxBgBR.Render(Control, iX + LengthTL + LengthM, iY, LengthBR, Rect.Height)
        else
          GfxBgBR.Render(Control, iX, iY + LengthTL + LengthM, Rect.Width, LengthBR);
      end;
    end;

    // Do we want to draw a foreground?
    if Assigned(GfxFgM) or Assigned(GfxFgTL) or Assigned(GfxFgBR) then
    begin
      // Determine width/height of top/left and bottom/right bar foreground
      if Assigned(GfxFgTL) then
        if Horizontal then LengthTL := GfxFgTL.Width else LengthTL := GfxFgTL.Height
      else LengthTL := 0;
      if Assigned(GfxFgBR) then
        if Horizontal then LengthBR := GfxFgBR.Width else LengthBR := GfxFgBR.Height
      else LengthBR := 0;

      // Reduce available bar area to allow for bar ends
      if Horizontal then BarWidth := BarWidth - LengthTL - LengthBR
                    else BarHeight := BarHeight - LengthTL - LengthBR;

      // Determine width/height of central bar foreground
      if Horizontal then LengthM := BarWidth else LengthM := BarHeight;
      if Assigned(GfxFgM) and (BarMax > 0) then // Prevent divide by zero error
        if FBarType = btProgress then
          LengthM := LengthM * BarPos / BarMax
        else
          LengthM := LengthM / BarMax
      else
        LengthM := 0;

      // Adjust position of foreground for scrollbars & sliders
      if (FBarType <> btProgress) and (BarMax > 0) then // Prevent divide by zero
        if Horizontal then BarX := BarX + (BarWidth * BarPos / BarMax)
                      else BarY := BarY + (BarHeight * BarPos / BarMax);

      // Render bar foreground
      if LengthTL > 0 then
      begin
        if Horizontal then
          GfxFgTL.Render(Control, BarX, BarY, LengthTL, BarHeight)
        else
          GfxFgTL.Render(Control, BarX, BarY, BarWidth, LengthTL);
      end;
      if Assigned(GfxFgM) and (LengthM > 0) then
      begin
        if Horizontal then
          GfxFgM.Render(Control, BarX + LengthTL, BarY, LengthM, BarHeight)
        else
          GfxFgM.Render(Control, BarX, BarY + LengthTL, BarWidth, LengthM);
      end;
      if LengthBR > 0 then
      begin
        if Horizontal then
          GfxFgBR.Render(Control, BarX + LengthTL + LengthM, BarY, LengthBR, BarHeight)
        else
          GfxFgBR.Render(Control, BarX, BarY + LengthTL + LengthM, BarWidth, LengthBR);
      end;
    end;
  end;
end;

procedure TAHMAbstractBar.SeedObject;
begin
{$IFDEF DESIGNER}
  if Ancestor = nil then
    with ApplicationManagerInstance.ThemeManager.VisualDescriptor do
    begin
      VirtualWidth := DEFAULT_CTRL_WIDTH;
      VirtualHeight := DEFAULT_CTRL_HEIGHT div 4;
      ImagesFgTL.AddObject(STATE_VAL_IDLE, ShapeList.GetShape(DEFAULT_CIRCLE_NAME));
      ImagesBgM.AddObject(STATE_VAL_IDLE, ShapeList.GetShape(DEFAULT_OBJECT_NAME));
    end;
{$ENDIF}
end;

procedure TAHMAbstractBar.Assign(Source: TPersistent);
begin
  inherited Assign(Source);

  with Source as TAHMAbstractBar do
  begin
    Self.VirtualTopMargin := VirtualTopMargin;
    Self.VirtualBottomMargin := VirtualBottomMargin;
    Self.VirtualLeftMargin := VirtualLeftMargin;
    Self.VirtualRightMargin := VirtualRightMargin;
    Self.BarType := BarType;
    Self.ImagesBgTL.Assign(ImagesBgTL);
    Self.ImagesBgM.Assign(ImagesBgM);
    Self.ImagesBgBR.Assign(ImagesBgBR);
    Self.ImagesFgTL.Assign(ImagesFgTL);
    Self.ImagesFgM.Assign(ImagesFgM);
    Self.ImagesFgBR.Assign(ImagesFgBR);
  end;
end;

procedure TAHMAbstractBar.SaveXMLBody(Lines: TStrings);
  procedure SaveImages(ImageType: String);
  var
    i: Integer;
    Images: TAHMUIStateObjectList;
  begin
    // Get state list for appropriate image type
    Images := GetImages(ImageType);
    if Assigned(Images) then
      for i := 0 to Pred(Images.Count) do
      begin
        Lines.Add(TABS[4] + TGB+TAG_IMAGE+SPC+ATTR_NAME+ATB + EncodeXML(ImageType) +
                  ATN + ATTR_STATE + ATB + EncodeXML(Images.States[i]) + ATC);
        Lines.Add(TABS[5] + Images.Objects[i].XMLReference);
        Lines.Add(TABS[4] + TGE+TAG_IMAGE+TGC);
      end;
  end;
var
  oAncs: TAHMAbstractBar;
begin
  // Determine which ancestor to compare against - real or empty
  oAncs := TAHMAbstractBar(ObjectToCompare);

  // Do we need to store foreground properties?
  if ((FVirtTopMargin <> 0) and (FVirtTopMargin <> oAncs.VirtualTopMargin)) or
     ((FVirtBottomMargin <> 0) and (FVirtBottomMargin <> oAncs.VirtualBottomMargin)) or
     ((FVirtLeftMargin <> 0) and (FVirtLeftMargin <> oAncs.VirtualLeftMargin)) or
     ((FVirtRightMargin <> 0) and (FVirtRightMargin <> oAncs.VirtualRightMargin)) then
  begin
    Lines.Add(TABS[3] + TGB+TAG_FOREGROUND+TGC);
    // Do we need to store vertical margins?
    if ((FVirtTopMargin <> 0) and (FVirtTopMargin <> oAncs.VirtualTopMargin)) or
       ((FVirtBottomMargin <> 0) and (FVirtBottomMargin <> oAncs.VirtualBottomMargin)) then
      Lines.Add(TABS[4] + TGB+TAG_VERT_MARGIN+TGC+ IntToStr(FVirtTopMargin) + ',' +
                IntToStr(FVirtBottomMargin) + TGE+TAG_VERT_MARGIN+TGC);
    // Do we need to store horizontal margins?
    if ((FVirtLeftMargin <> 0) and (FVirtLeftMargin <> oAncs.VirtualLeftMargin)) or
       ((FVirtRightMargin <> 0) and (FVirtRightMargin <> oAncs.VirtualRightMargin)) then
      Lines.Add(TABS[4] + TGB+TAG_HORZ_MARGIN+TGC+ IntToStr(FVirtLeftMargin) + ',' +
                IntToStr(FVirtRightMargin) + TGE+TAG_HORZ_MARGIN+TGC);
    Lines.Add(TABS[3] + TGE+TAG_FOREGROUND+TGC);
  end;

  // Do we need to store size?
  if ((VirtualWidth <> 0) and (VirtualWidth <> oAncs.VirtualWidth)) or
     ((VirtualHeight <> 0) and (VirtualHeight <> oAncs.VirtualHeight)) then
    Lines.Add(TABS[3] + TGB+TAG_SIZE+TGC+ IntToStr(VirtualWidth) + ',' +
              IntToStr(VirtualHeight) + TGE+TAG_SIZE+TGC);

  // Do we need to store images?
  if FImagesBgTL.IsDifferent(oAncs.ImagesBgTL) or
     FImagesBgM.IsDifferent(oAncs.ImagesBgM) or
     FImagesBgBR.IsDifferent(oAncs.ImagesBgBR) or
     FImagesFgTL.IsDifferent(oAncs.ImagesFgTL) or
     FImagesFgM.IsDifferent(oAncs.ImagesFgM) or
     FImagesFgBR.IsDifferent(oAncs.ImagesFgBR) then
  begin
    Lines.Add(TABS[3] + TGB+TAG_IMAGES+TGC);
    SaveImages(BARIMG_VAL_BGSTART);
    SaveImages(BARIMG_VAL_BGMIDDLE);
    SaveImages(BARIMG_VAL_BGFINISH);
    SaveImages(BARIMG_VAL_FGSTART);
    SaveImages(BARIMG_VAL_FGMIDDLE);
    SaveImages(BARIMG_VAL_FGFINISH);
    Lines.Add(TABS[3] + TGE+TAG_IMAGES+TGC);
  end;

  // Do we need to store bar type?
  if (FBarType <> oAncs.BarType) then
    if FBarType = btProgress then
      Lines.Add(TABS[3] + TGB+TAG_BARTYPE+SPC+ATTR_MODE+ATB+ATTR_VAL_PROGRESS+ATE)
    else if FBarType = btScroll then
      Lines.Add(TABS[3] + TGB+TAG_BARTYPE+SPC+ATTR_MODE+ATB+ATTR_VAL_SCROLL+ATE)
    else
      Lines.Add(TABS[3] + TGB+TAG_BARTYPE+SPC+ATTR_MODE+ATB+ATTR_VAL_SLIDER+ATE);
end;

procedure TAHMAbstractBar.ParseFromXML(XML: TAHMXMLHelper);
var
  sName, sState, sDest, sMode: String;
  iX, iY: Integer;
  BarNode, PropsNode, StateNode, DetailNode: TAHMXMLNode;
begin
  // Initialise parsing nodes
  BarNode := XML.CurrentNode;
  PropsNode := nil;

  // All bar elements are optional, so see what we have
  while XML.NextElement(BarNode, PropsNode) do
  begin
    if XML.CurrentElement = TAG_FOREGROUND then
    begin
      // Handle bar foreground properties
      DetailNode := nil;
      while XML.NextElement(PropsNode, DetailNode) do
      begin
        if XML.CurrentElement = TAG_VERT_MARGIN then
        begin
          if XML.GetCoordsValue(iX, iY) then
          begin
            VirtualTopMargin := iX;
            VirtualBottomMargin := iY;
          end
          else
            LogParsingError(THM_PRS_VIS_MALFORMED_BAR_VMARGIN_TAGS, Self);
        end
        else if XML.CurrentElement = TAG_HORZ_MARGIN then
        begin
          if XML.GetCoordsValue(iX, iY) then
          begin
            VirtualLeftMargin := iX;
            VirtualRightMargin := iY;
          end
          else
            LogParsingError(THM_PRS_VIS_MALFORMED_BAR_HMARGIN_TAGS, Self);
        end
        else
          LogParsingError(THM_PRS_VIS_UNSUPPORTED_BAR_FGRND_PROP, Self);
      end;
    end
    else if XML.CurrentElement = TAG_SIZE then
    begin
      if XML.GetCoordsValue(iX, iY) then
      begin
        VirtualWidth := iX;
        VirtualHeight := iY;
      end
      else
        LogParsingError(THM_PRS_VIS_MALFORMED_BAR_SIZE_TAGS, Self);
    end
    else if XML.CurrentElement = TAG_IMAGES then
    begin
      // Handle bar images
      StateNode := nil;
      while XML.FindElement(PropsNode, StateNode, TAG_IMAGE) do
      begin
        sName := XML.GetAttribute(ATTR_NAME);
        sState := XML.GetAttribute(ATTR_STATE);
        DetailNode := nil;

        // Look for bitmap or shape tag
        while XML.NextElement(StateNode, DetailNode) do
        begin
          sDest := XML.GetAttribute(ATTR_NAME);
          if XML.CurrentElement = TAG_SHAPE then
            AddImage(sState, sName, sDest, otShapes)
          else if XML.CurrentElement = TAG_BITMAP then
            AddImage(sState, sName, sDest, otBitmaps)
          else
            LogParsingError(THM_PRS_VIS_MALFORMED_BAR_IMAGE_TAGS, Self);
        end;
      end;
    end
    else if XML.CurrentElement = TAG_BARTYPE then
    begin
      // Get bar type
      sMode := XML.GetAttribute(ATTR_MODE);
      if sMode = ATTR_VAL_SCROLL then BarType := btScroll
      else if sMode = ATTR_VAL_PROGRESS then BarType := btProgress
      else if sMode = ATTR_VAL_SLIDER then BarType := btSlider;
    end;
  end;
end;

procedure TAHMAbstractBar.BuildProperties(Properties: TAHMUIPropertyList);
begin
{$IFDEF DESIGNER}
  inherited;

  // Build custom button properties
  Properties.Add('Bar Type', 'BarType', CAT_DESIGN, HINT_BARTYPE);
  Properties.Add('Top Margin', 'VirtualTopMargin', CAT_DESIGN, HINT_TOPMARGIN);
  Properties.Add('Bottom Margin', 'VirtualBottomMargin', CAT_DESIGN, HINT_BOTTOMMARGIN);
  Properties.Add('Left Margin', 'VirtualLeftMargin', CAT_DESIGN, HINT_LEFTMARGIN);
  Properties.Add('Right Margin', 'VirtualRightMargin', CAT_DESIGN, HINT_RIGHTMARGIN);
  Properties.Add('Width', 'VirtualWidth', CAT_DESIGN, HINT_WIDTH);
  Properties.Add('Height', 'VirtualHeight', CAT_DESIGN, HINT_HEIGHT);
  Properties.Add('Top/Left', 'ImagesBgTL', CAT_BGIMAGES, HINT_BGIMAGESTL);
  Properties.Add('Middle', 'ImagesBgM', CAT_BGIMAGES, HINT_BGIMAGESM);
  Properties.Add('Bottom/Right', 'ImagesBgBR', CAT_BGIMAGES, HINT_BGIMAGESBR);
  Properties.Add('Top/Left', 'ImagesFgTL', CAT_FGIMAGES, HINT_FGIMAGESTL);
  Properties.Add('Middle', 'ImagesFgM', CAT_FGIMAGES, HINT_FGIMAGESM);
  Properties.Add('Bottom/Right', 'ImagesFgBR', CAT_FGIMAGES, HINT_FGIMAGESBR);
{$ENDIF}
end;

class function TAHMAbstractBar.ClassName: ShortString;
begin
  // Remove 'Abstract' prefix
  Result := 'Bar';
end;


end.
